<script lang="ts" setup>
import type { EchartsUIType } from '@croco/plugins/echarts';

import type { MetricData, MetricResponse } from './types';

import { onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, onMounted, reactive, ref } from 'vue';

import { Page } from '@croco/framework/libraries';
import * as axios from '@croco/plugins/axios';
import { EchartsUI, useEcharts } from '@croco/plugins/echarts';

import dayjs from 'dayjs';

import { service } from '#/api/request';

import { desc } from './data';

defineOptions({ name: 'MonitorServer' });

const pageRef = ref();
const memoryChartRef = ref<EchartsUIType>();
const cpuUsageChartRef = ref<EchartsUIType>();
const memoryEcharts = useEcharts(memoryChartRef);
const cpuUsageEcharts = useEcharts(cpuUsageChartRef);

const dataSource = ref<Array<MetricData>>([]);
const datetime = ref();
const cpuUsage = reactive<{ cdata: string[]; data: string[]; xdata: string[] }>({
    data: ['0'],
    xdata: ['0'],
    cdata: ['0'],
});

const memory = reactive<{ data: string[]; xdata: string[] }>({
    data: ['0'],
    xdata: ['0'],
});

const interval = ref<ReturnType<typeof setInterval>>();
let isActive = false;
const initEchartsHandle = () => {
    if (isActive) return;
    isActive = true;
    interval.value = setInterval(() => {
        axios
            .all([service.get('/actuator/metrics/system.cpu.usage'), service.get('/actuator/metrics/process.cpu.usage'), service.get('/actuator/metrics/jvm.memory.used')])
            .then((rs) => {
                rs[0].measurements.push(rs[1].measurements[0]);
                renderCpuUsageEcharts(rs[0]);
                renderMemoryEcharts(rs[2]);
            })
            .catch((error) => {
                console.error(error);
                stop();
            });
    }, 1000);
};
const renderMemoryEcharts = (r: any) => {
    const currentMemory = Number(r.measurements[0].value / 1_048_576).toFixed(2);
    const time = dayjs(new Date()).format('mm:ss');
    memory.data.push(currentMemory);
    memory.xdata.push(time);
    if (memory.data.length >= 60) {
        memory.data.shift();
        memory.xdata.shift();
    }
    const memoryChartInstance = memoryEcharts.getChartInstance();
    if (memoryChartInstance) {
        memoryChartInstance?.setOption({
            series: [
                {
                    data: memory.data,
                },
            ],
            xAxis: {
                data: memory.xdata,
            },
        });
    } else {
        memoryEcharts.renderEcharts({
            title: {
                text: 'JVM内存使用情况',
            },
            grid: {
                bottom: 0,
                containLabel: true,
                left: '1%',
                right: '1%',
            },
            tooltip: {
                axisPointer: {
                    lineStyle: {
                        color: '#019680',
                        width: 1,
                    },
                },
                trigger: 'axis',
            },
            xAxis: {
                type: 'category',
                splitLine: {
                    lineStyle: {
                        type: 'solid',
                        width: 1,
                    },
                    show: true,
                },
                data: [],
                axisTick: {
                    show: false,
                },
                boundaryGap: false,
            },
            yAxis: {
                axisTick: {
                    show: false,
                },
                min: 0, // 固定最小值为0
                axisLabel: {
                    formatter: '{value}', // 在刻度标签后加上百分号
                },
                splitArea: {
                    show: true,
                },
                type: 'value',
            },
            series: [
                {
                    name: 'JVM内存使用',
                    areaStyle: {},
                    data: [],
                    itemStyle: {
                        color: '#5ab1ef',
                    },
                    smooth: true,
                    type: 'line',
                },
            ],
        });
    }
};
const renderCpuUsageEcharts = (r: any) => {
    let currentMemory = r.measurements[0].value * 100;
    const time = dayjs(new Date()).format('mm:ss');
    cpuUsage.data.push(Number(currentMemory).toFixed(2));
    cpuUsage.xdata.push(time);
    if (cpuUsage.data.length >= 60) {
        cpuUsage.data.shift();
        cpuUsage.xdata.shift();
    }
    currentMemory = r.measurements[1].value * 100;
    cpuUsage.cdata.push(Number(currentMemory).toFixed(2));
    if (cpuUsage.cdata.length >= 60) {
        cpuUsage.cdata.shift();
    }
    const cpuUsageChartInstance = cpuUsageEcharts.getChartInstance();
    if (cpuUsageChartInstance) {
        cpuUsageChartInstance?.setOption({
            series: [
                {
                    data: cpuUsage.data,
                },
                {
                    data: cpuUsage.cdata,
                },
            ],
            xAxis: {
                data: cpuUsage.xdata,
            },
        });
    } else {
        cpuUsageEcharts.renderEcharts({
            title: {
                text: 'CPU使用率',
            },
            grid: {
                bottom: 0,
                containLabel: true,
                left: '1%',
                right: '1%',
            },
            tooltip: {
                axisPointer: {
                    lineStyle: {
                        color: '#019680',
                        width: 1,
                    },
                },
                trigger: 'axis',
            },
            xAxis: {
                type: 'category',
                splitLine: {
                    lineStyle: {
                        type: 'solid',
                        width: 1,
                    },
                    show: true,
                },
                data: [],
                axisTick: {
                    show: false,
                },
                boundaryGap: false,
            },
            yAxis: {
                axisTick: {
                    show: false,
                },
                min: 0, // 固定最小值为0
                max: 100, // 固定最大值为100
                axisLabel: {
                    formatter: '{value}%', // 在刻度标签后加上百分号
                },
                splitArea: {
                    show: true,
                },
                type: 'value',
            },
            series: [
                {
                    name: 'CPU使用率',
                    areaStyle: {},
                    data: [],
                    itemStyle: {
                        color: '#5ab1ef',
                    },
                    smooth: true,
                    type: 'line',
                },
                {
                    name: '当前进程CPU使用率',
                    areaStyle: {},
                    data: [],
                    itemStyle: {
                        color: '#019680',
                    },
                    smooth: true,
                    type: 'line',
                },
            ],
        });
    }
};

const onRefresh = () => {
    const date = new Date();
    datetime.value = `${date.getFullYear()}年${date.getMonth()}月${date.getDay()}日${date.getHours()}时${date.getMinutes()}分${date.getSeconds()}秒`;
    axios
        .all([
            service.get<MetricResponse>('/actuator/metrics/system.cpu.count'),
            service.get<MetricResponse>('/actuator/metrics/system.cpu.usage'),
            service.get<MetricResponse>('/actuator/metrics/process.uptime'),
            service.get<MetricResponse>('/actuator/metrics/process.start.time'),
            service.get<MetricResponse>('/actuator/metrics/process.cpu.usage'),
            service.get<MetricResponse>('/actuator/metrics/jvm.memory.max'),
            service.get<MetricResponse>('/actuator/metrics/jvm.memory.committed'),
            service.get<MetricResponse>('/actuator/metrics/jvm.memory.used'),
            service.get<MetricResponse>('/actuator/metrics/jvm.buffer.memory.used'),
            service.get<MetricResponse>('/actuator/metrics/jvm.buffer.count'),
            service.get<MetricResponse>('/actuator/metrics/jvm.threads.daemon'),
            service.get<MetricResponse>('/actuator/metrics/jvm.threads.live'),
            service.get<MetricResponse>('/actuator/metrics/jvm.threads.peak'),
            service.get<MetricResponse>('/actuator/metrics/jvm.classes.loaded'),
            service.get<MetricResponse>('/actuator/metrics/jvm.classes.unloaded'),
            service.get<MetricResponse>('/actuator/metrics/jvm.gc.memory.allocated'),
            service.get<MetricResponse>('/actuator/metrics/jvm.gc.memory.promoted'),
            service.get<MetricResponse>('/actuator/metrics/jvm.gc.max.data.size'),
            service.get<MetricResponse>('/actuator/metrics/jvm.gc.live.data.size'),
            service.get<MetricResponse>('/actuator/metrics/jvm.gc.pause'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.sessions.created'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.sessions.expired'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.sessions.active.current'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.sessions.active.max'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.sessions.rejected'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.global.sent'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.global.request.max'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.global.request'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.global.request'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.global.request.max'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.threads.current'),
            service.get<MetricResponse>('/actuator/metrics/tomcat.threads.config.max'),
        ])
        .then((r) => {
            dataSource.value = [];
            r.forEach((item) => {
                for (let i = 0; i < item.measurements.length; i++) {
                    const obj = {} as MetricData;
                    const measurement = item.measurements[i];
                    obj.name = measurement?.statistic === 'VALUE' ? item.name : `${item.name}.${measurement?.statistic?.toLowerCase()}`;
                    const dc = desc[obj.name];
                    obj.value = (dc?.convert ? dc?.convert(measurement?.value) : measurement?.value) + (dc?.unit ?? '');
                    obj.description = dc?.desc ?? '';
                    dataSource.value.push(obj);
                }
            });
        })
        .catch((error) => {
            pageRef.value?.setApiState(error);
        });
};

const stop = () => {
    if (interval.value) {
        clearInterval(interval.value);
        isActive = false;
        interval.value = undefined;
    }
};

onBeforeMount(() => onRefresh());

onBeforeUnmount(() => stop());

onActivated(() => initEchartsHandle());

onDeactivated(() => stop());

onMounted(() => initEchartsHandle());
</script>
<template>
    <Page ref="pageRef" auto-content-height>
        <div class="server-info">
            <el-card :body-style="{ padding: '10px' }" shadow="hover">
                <el-row>
                    <el-col :span="12">
                        <EchartsUI ref="cpuUsageChartRef" height="40vh" />
                    </el-col>
                    <el-col :span="12">
                        <EchartsUI ref="memoryChartRef" height="40vh" />
                    </el-col>
                </el-row>
            </el-card>
            <div class="alert">
                <el-alert :closable="false" :show-icon="true" type="success">
                    数据获取时间 {{ datetime }}
                    <a style="position: absolute; right: 35px; cursor: pointer" @click="onRefresh">点击刷新</a>
                </el-alert>
            </div>
            <el-table :data="dataSource" style="margin-top: 5px">
                <el-table-column label="参数" prop="name" />
                <el-table-column label="描述" prop="description" />
                <el-table-column label="当前值" prop="value" />
            </el-table>
        </div>
    </Page>
</template>
<style lang="scss" scoped>
.server-info {
    width: 100%;
}
</style>
